﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Xml.Linq;

using ApocDev.TownsApi.Aspects;

namespace ApocDev.TownsApi.XmlFiles
{
    [NotifyPropertyChanged]
    public class ItemsXml : ObservableCollection<Item>
    {
        public static int NumLoaded { get; set; }
        public static int TotalToLoad { get; set; }

        public ItemsXml(string path)
        {
            XElement root = XElement.Load(path);
            var items = root./*Element("items").*/Elements();
            TotalToLoad = items.Count();
            ModFiles.FireLoadEvent(this, new FileLoadEventArgs("Items", TotalToLoad, 0));
            foreach (var xml in items)
            {
                Add(new Item(xml));
                ModFiles.FireLoadEvent(this, new FileLoadEventArgs("Items", TotalToLoad, ++NumLoaded));
            }
        }

        public XElement Save()
        {
            XElement root = new XElement("items");
            foreach (var item in this)
            {
                root.Add(item.Save());
            }
            return root;
        }
    }
    [NotifyPropertyChanged]
    public class Item
    {
        public override string ToString()
        {
            if (string.IsNullOrWhiteSpace(Name))
                return ID;
            return Name + " (" + ID + ")";
        }
        public ObservableCollection<Effect> TrapEffects { get; set; }
        public Item(XElement xml)
        {
            ID = xml.Name.LocalName;
            // Default to a non-null list. Slightly more mem usage, but no need to worry about null checks when enumerating
            //Actions = new ObservableCollection<Action>();
            Habitats = new ObservableCollection<Terrain>();
            Description = new ObservableCollection<string>();
            FoodEffects = new ObservableCollection<Effect>();
            Zones = new ObservableCollection<Zone>();
            WearEffects = new ObservableCollection<Effect>();
            TrapEffects = new ObservableCollection<Effect>();
            foreach (var e in xml.Elements())
            {
                switch (e.Name.LocalName.ToLower())
                {
                    case "name":
                        Name = e.Value;
                        break;
                    case "description":
                        Description.Add(e.Value);
                        break;
                    case "type":
                        Type = ModFiles.GetType(e.Value);
                        break;
                    case "container":
                        Container = e.Value.ToLower() == "true";
                        break;
                    case "containersize":
                        ContainerSize = int.Parse(e.Value);
                        break;
                    case "value":
                        Value = int.Parse(e.Value);
                        break;
                    case "stackable":
                        Stackable = e.Value.ToLower() == "true";
                        break;
                    case "stackablesize":
                        StackableSize = int.Parse(e.Value);
                        break;
                    case "buildingtime":
                        BuildingTime = int.Parse(e.Value);
                        break;
                    case "canbeeaten":
                        CanBeEaten = e.Value.ToLower() == "true";
                        break;
                    case "foodvalue":
                        FoodValue = int.Parse(e.Value);
                        break;
                    case "foodfillpct":
                        FoodFillPercent = int.Parse(e.Value);
                        break;
                    case "foodeattime":
                        FoodEatTime = int.Parse(e.Value);
                        break;
                    case "maxage":
                        MaxAge = new RandomRoll(e.Value);
                        break;
                    case "maxageitem":
                        // Since this is kind of recursive, we need to lazy-load things a little bit.
                        // We'll only set the ID, and when we need the actual item itself, we'll do the lookup.
                        _maxAgeItemId = e.Value;
                        break;
                    case "locked":
                        Locked = e.Value.ToLower() == "true";
                        break;
                    case "action":
                        _actionIds.Add(e.Value);
                        break;
                    case "habitat":
                        Habitats.Add(ModFiles.GetTerrain(e.Value));
                        break;
                    case "spawn":
                        _spawnItemId = e.Value;
                        break;
                    case "spawnmaxitems":
                        SpawnMaxItems = int.Parse(e.Value);
                        break;
                    case "spawnturns":
                        SpawnTurns = new RandomRoll(e.Value);
                        break;
                    case "ranged":
                        Ranged = e.Value.ToLower() == "true";
                        break;
                    case "rangedammo":
                        _rangedAmmoId = e.Value;
                        break;
                    case "rangedcharges":
                        if (e.Value == "INFINITE")
                            RangedCharges = -1;
                        else
                            RangedCharges = int.Parse(e.Value);
                        break;
                    case "location":
                        Location = e.Value;
                        break;
                    case "attack":
                        Attack = new RandomRoll(e.Value);
                        break;
                    case "defense":
                        Defense = new RandomRoll(e.Value);
                        break;
                    case "damage":
                        Damage = new RandomRoll(e.Value);
                        break;
                    case "los":
                        LOS = new RandomRoll(e.Value);
                        break;
                    case "verb":
                        Verb = e.Value;
                        break;
                    case "verbinfinitive":
                        VerbInfinitive = e.Value;
                        break;
                    case "zone":
                        Zones.Add(ModFiles.GetZone(e.Value));
                        break;
                    case "tags":
                        Tags = e.Value;
                        break;
                    case "glue":
                        Glue = e.Value.ToLower() == "true";
                        break;
                    case "base":
                        Base = e.Value.ToLower() == "true";
                        break;
                    case "blocky":
                        Blocky = e.Value.ToLower() == "true";
                        break;
                    case "alwaysoperative":
                        AlwaysOperative = e.Value.ToLower() == "true";
                        break;
                    case "wall":
                        Wall = e.Value.ToLower() == "true";
                        break;
                    case "canbebuiltonholes":
                        CanBeBuiltOnHoles = e.Value.ToLower() == "true";
                        break;
                    case "zonemergerup":
                        ZoneMergerUp = e.Value.ToLower() == "true";
                        break;
                    case "wallconnector":
                        WallConnector = e.Value.ToLower() == "true";
                        break;
                    case"usedtosleep":
                        UsedToSleep = e.Value.ToLower() == "true";
                        break;
                    case "foodeffects":
                        foreach (var eff in e.Value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                                   .Select(s => s.Trim()))
                        {
                            FoodEffects.Add(ModFiles.GetEffect(eff));
                        }
                        break;
                    case "happiness":
                        Happiness = int.Parse(e.Value);
                        break;
                    case "floorwalkspeed":
                        FloorWalkSpeed = int.Parse(e.Value);
                        break;
                    case "habitatgroup":
                        HabitatGroup = e.Value;
                        break;
                    case "usedtosit":
                        UsedToSit = e.Value.ToLower() == "true";
                        break;
                    case "door":
                        Door = e.Value.ToLower() == "true";
                        break;
                    case "level":
                        Level = int.Parse(e.Value);
                        break;
                    case "speeduppct":
                        SpeedUpPercent = int.Parse(e.Value);
                        break;
                    case "attackspeed":
                        AttackSpeed = int.Parse(e.Value);
                        break;
                    case "weareffects":
                        foreach (var eff in e.Value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                                   .Select(s => s.Trim()))
                        {
                            WearEffects.Add(ModFiles.GetEffect(eff));
                        }
                        break;
                    case "walkspeed":
                        WalkSpeed = int.Parse(e.Value);
                        break;
                    case "trap":
                        Trap = e.Value.ToLower() == "true";
                        break;
                    case "trapeffects":
                        foreach (var eff in e.Value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                                   .Select(s => s.Trim()))
                        {
                            TrapEffects.Add(ModFiles.GetEffect(eff));
                        }
                        break;
                    case "trapcooldown":
                        TrapCooldown = new RandomRoll(e.Value);
                        break;
                    case "traponicon":
                        TrapOnIcon = e.Value;
                        break;
                    case "traptargets":
                        TrapTargets = e.Value;
                        break;
                    default:
                        MessageBox.Show("Unknown element for " + GetType().Name + ": " + e.Name.LocalName);
                        break;
                }
            }

        }
        public XElement Save()
        {
            return null;
        }

        public string TrapTargets { get; set; }
        public RandomRoll TrapCooldown { get; set; }
        public string TrapOnIcon { get; set; }
        public bool Trap { get; set; }
        public int WalkSpeed { get; set; }
        public ObservableCollection<Effect> WearEffects { get; set; }
        public bool Door { get; set; }
        public int Level { get; set; }
        public int SpeedUpPercent { get; set; }
        public int AttackSpeed { get; set; }

        public bool UsedToSit { get; set; }

        public int Happiness { get; set; }
        public int FloorWalkSpeed { get; set; }
        public string HabitatGroup { get; set; }

        // Walls
        public bool Glue { get; set; }
        public bool Base { get; set; }
        public bool Blocky { get; set; }
        public bool AlwaysOperative { get; set; }
        public bool Wall { get; set; }
        public bool CanBeBuiltOnHoles { get; set; }
        public bool ZoneMergerUp { get; set; }
        public bool WallConnector { get; set; }
        public bool UsedToSleep { get; set; }

        public ObservableCollection<Effect> FoodEffects { get; set; }

        public string ID { get; set; }

        public string Name { get; set; }
        public ObservableCollection<string> Description { get; set; }
        public Type Type { get; set; }
        public bool Container { get; set; }
        public int ContainerSize { get; set; }
        public int Value { get; set; }
        public bool Stackable { get; set; }
        public int StackableSize { get; set; }
        public int BuildingTime { get; set; }

        // Food
        public bool CanBeEaten { get; set; }
        public int FoodValue { get; set; }
        public int FoodFillPercent { get; set; }
        public int FoodEatTime { get; set; }

        public RandomRoll MaxAge { get; set; }
        private string _maxAgeItemId;
        private Item _maxAgeItem;
        public Item MaxAgeItem
        {
            get { return _maxAgeItem ?? ModFiles.GetItem(_maxAgeItemId); }
            set
            {
                _maxAgeItem = value;
                _maxAgeItemId = value.ID;
            }
        }
        public bool Locked { get; set; }

        private List<string> _actionIds = new List<string>();
        private ObservableCollection<Action> _actions;

        public ObservableCollection<Action> Actions
        {
            get
            {
                if (_actions == null)
                {
                    _actions = new ObservableCollection<Action>();
                    foreach (var actionId in _actionIds)
                    {
                        _actions.Add(ModFiles.GetAction(actionId));
                    }
                }
                return _actions;
            }
            set { _actions = value; }
        }

        public ObservableCollection<Terrain> Habitats { get; set; }


        private string _spawnItemId;
        private Item _spawnItem;
        public Item Spawn
        {
            get { return _spawnItem ?? ModFiles.GetItem(_spawnItemId); }
            set
            {
                _spawnItem = value;
                _spawnItemId = value.ID;
            }
        }

        public int SpawnMaxItems { get; set; }
        public RandomRoll SpawnTurns { get; set; } // 1d57600+28800

        // Armor/Weapons
        public bool Ranged { get; set; }
        private string _rangedAmmoId;
        private Item _rangedAmmoItem;
        public Item RangedAmmo
        {
            get { return _rangedAmmoItem ?? ModFiles.GetItem(_rangedAmmoId); }
            set
            {
                _rangedAmmoItem = value;
                _rangedAmmoId = value.ID;
            }
        }
        public int RangedCharges { get; set; }
        public string Location { get; set; } // WEAPON, ARMOR
        public RandomRoll Attack { get; set; } // 1d444+428
        public RandomRoll Defense { get; set; }
        public RandomRoll Damage { get; set; }

        public RandomRoll LOS { get; set; }

        public string Verb { get; set; }
        public string VerbInfinitive { get; set; }
        public string Tags { get; set; }
        public ObservableCollection<Zone> Zones { get; set; }
    }
}
